package com.zyh.study.ForestPlus;

import com.dtflys.forest.Forest;
import com.dtflys.forest.backend.ContentType;
import com.dtflys.forest.config.ForestConfiguration;
import com.dtflys.forest.http.ForestRequest;
import com.dtflys.forest.utils.TypeReference;
import com.zeromarkets.security.st.oms.application.gateway.ExecutionBrokerClient;
import com.zeromarkets.security.st.oms.application.model.dto.AssetDTO;
import com.zeromarkets.security.st.oms.application.model.dto.OrderFilterDTO;
import com.zeromarkets.security.st.oms.infrastructure.broker.forestIntercepter.CommonForestInterceptor;
import com.zeromarkets.security.st.oms.infrastructure.config.AlpacaConfig;
import com.zeromarkets.security.st.oms.infrastructure.model.dto.AccountDTO;
import com.zeromarkets.security.st.oms.infrastructure.model.dto.BrokerOrderDTO;
import com.zeromarkets.security.st.oms.infrastructure.model.dto.BrokerUpdateOrderDTO;
import com.zeromarkets.security.st.oms.infrastructure.model.dto.OrderDTO;
import com.zeromarkets.security.st.oms.infrastructure.model.dto.OrderRespDTO;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * alpaca执行接口实现
 *
 * @author Lunyue Chen
 */
@Component
public class AlpacaExecutionClient implements ExecutionBrokerClient {

    // Forest 配置对象，用于全局配置 HTTP 客户端参数
    private final ForestConfiguration forestConfig;

    // 认证头缓存
    private  String authHeader;

    // 提取请求头
    public static final String ACCEPT = "accept";
    public static final String AUTHORIZATION = "authorization";
    public static final String CONTENT_TYPE = "content-type";

    @Autowired
    private AlpacaConfig config;

    public AlpacaExecutionClient() {
        // 获取 Forest 全局配置实例
        // Forest.config() 返回单例配置对象，可用于设置全局参数
        forestConfig = Forest.config();
        // 配置全局参数，开启请求日志，便于调试
        forestConfig.setLogEnabled(true);
        // 设置连接超时时间为 15 秒
        forestConfig.setConnectTimeout(15 * 1000);
        // 设置读取超时时间为 15 秒
        forestConfig.setReadTimeout(15 * 1000);
        forestConfig.setBackendName("httpclient");
    }

    @PostConstruct
    public void init() {
        // 预构建Basic Auth认证头
        String credentials = config.getBrokerApiKey() + ":" + config.getBrokerApiSecret();
        authHeader = "Basic " + Base64.getEncoder().encodeToString(credentials.getBytes());
    }

    private ForestRequest<?> prepareRequest(ForestRequest<?> request) {
        return request
                .addInterceptor(CommonForestInterceptor.class)
                .addHeader(ACCEPT, ContentType.APPLICATION_JSON)
                .addHeader(AUTHORIZATION, authHeader)
                .addHeader(CONTENT_TYPE, ContentType.APPLICATION_JSON)
                .maxRetryCount(3)
                .maxRetryInterval(100L)
                .connectTimeout(15 * 1000)
                .readTimeout(15 * 1000);
    }

    @Override
    public String create(OrderDTO order, AccountDTO account) {
        String url = config.getBrokerBaseUrl() + "/v1/trading/accounts/" +  account.getBrokerAccountId() + "/orders";
        OrderRespDTO orderRespDTO = prepareRequest(Forest.post(url)).addBody(order).execute(OrderRespDTO.class);
        return orderRespDTO.getOrderId();
    }

    @Override
    public String update(BrokerUpdateOrderDTO updateOrderDTO, AccountDTO account, BrokerOrderDTO brokerOrderDTO) {
        //https://broker-api.sandbox.alpaca.markets/v1/trading/accounts/{account_id}/orders/{order_id}
        String url = config.getBrokerBaseUrl() + "/v1/trading/accounts/" + account.getBrokerAccountId() + "/orders" + brokerOrderDTO.getBrokerOrderId();
        OrderRespDTO orderRespDTO = prepareRequest(Forest.patch(url)).addBody(updateOrderDTO).execute(OrderRespDTO.class);
        return orderRespDTO.getClientOrderId();
    }

    @Override
    public void cancel(AccountDTO account, BrokerOrderDTO brokerOrderDTO) {
        //https://broker-api.sandbox.alpaca.markets/v1/trading/accounts/{account_id}/orders/{order_id}
        String url = config.getBrokerBaseUrl() + "/v1/trading/accounts/" + account.getBrokerAccountId() + "/orders" + brokerOrderDTO.getBrokerOrderId();
        prepareRequest(Forest.delete(url)).execute();

    }

    @Override
    public void cancelAllOrders(AccountDTO account) {
        //https://broker-api.sandbox.alpaca.markets/v1/trading/accounts/{account_id}/orders
        String url = config.getBrokerBaseUrl() + "/v1/trading/accounts/" + account.getBrokerAccountId() + "/orders";
        prepareRequest(Forest.delete(url)).execute();
    }

    @Override
    public void closePosition(AccountDTO account, AssetDTO asset, Double quantity, Double percentage) {
        // https://broker-api.sandbox.alpaca.markets/v1/trading/accounts/{account_id}/positions/{symbol_or_asset_id}
        Map<String, Object> requestBody = new HashMap<>();
        requestBody.put("qty", quantity);
        requestBody.put("percentage", percentage);
        String url = config.getBrokerBaseUrl() + "/v1/trading/accounts/" + account.getBrokerAccountId() + "/positions" + asset.getBrokerSymbolOrAssetId();
        prepareRequest(Forest.delete(url)).addBody(requestBody).execute();
    }

    @Override
    public void closeAllPositions(AccountDTO account) {
        // https://broker-api.sandbox.alpaca.markets/v1/trading/accounts/{account_id}/positions
        Map<String, Object> requestBody = new HashMap<>();
        requestBody.put("cancel_orders", true);
        String url = config.getBrokerBaseUrl() + "/v1/trading/accounts/" + account.getBrokerAccountId() + "/positions";
        prepareRequest(Forest.delete(url)).execute();
    }

    @Override
    public OrderRespDTO queryOrderById(AccountDTO account, String orderId) {
        // https://broker-api.sandbox.alpaca.markets/v1/trading/accounts/{account_id}/orders/{order_id}
        Map<String, Object> requestBody = new HashMap<>();
        requestBody.put("cancel_orders", true);
        String url = config.getBrokerBaseUrl() + "/v1/trading/accounts/" + account.getBrokerAccountId() + "/orders" + orderId;
        OrderRespDTO orderRespDTO = prepareRequest(Forest.get(url)).addBody(requestBody)
                .execute(OrderRespDTO.class);
        return orderRespDTO;
    }

    @Override
    public List<OrderRespDTO> queryOrders(AccountDTO account, OrderFilterDTO orderFilterDTO) {
        //https://broker-api.sandbox.alpaca.markets/v1/trading/accounts/{account_id}/orders
        Map<String, Object> requestBody = new HashMap<>();
        requestBody.put("cancel_orders", true);
        String url = config.getBrokerBaseUrl() + "/v1/trading/accounts/" + account.getBrokerAccountId() + "/orders";
        //TODO 返回的List<OrderRespDTO>
        List<OrderRespDTO> orderRespDTOs = prepareRequest(Forest.get(url)).addBody(requestBody)
                .execute(new TypeReference<List<OrderRespDTO>>() {
                });
        return orderRespDTOs;
    }
}
